package org.teamswift.crow.rbac.cmd;

import com.google.common.base.Strings;
import lombok.Getter;
import org.jline.reader.LineReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.transaction.annotation.Transactional;
import org.teamswift.crow.rbac.entity.CrowOrganization;
import org.teamswift.crow.rbac.entity.CrowUser;
import org.teamswift.crow.rbac.repository.CrowOrganizationRepository;
import org.teamswift.crow.rbac.repository.CrowUserRepository;
import org.teamswift.crow.rbac.security.PasswordHelper;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Getter
@ShellComponent
public class CrowRbacInstallation  {

    private final CrowUserRepository crowUserRepository;

    private final CrowOrganizationRepository crowOrganizationRepository;

    private final String lockFile = String.format("%s%s%s", System.getProperty("user.dir"), File.separator, "INSTALL.LOCK");

    @Lazy
    @Autowired
    private LineReader lineReader;

    public CrowRbacInstallation(CrowUserRepository crowUserRepository,
                                CrowOrganizationRepository crowOrganizationRepository) {
        this.crowUserRepository = crowUserRepository;
        this.crowOrganizationRepository = crowOrganizationRepository;
    }

    @ShellMethod(
            value = "Initialize the RBAC system",
            group = "Crow"
    )
    public void rbacInstall() {
        if(!isInstalled()) {
            try {
                install();
            } catch (IOException e) {
                System.out.println("An error occurred while run installation: " + e.getMessage());
                e.printStackTrace();
            }
        }

        System.out.println("Your application was already installed, please remove the INSTALL.LOCK first to re-install the crow-rbac");
    }

    public boolean isInstalled() {
        File lock = new File(lockFile);
        return lock.exists();
    }

    @Transactional(rollbackFor = Exception.class)
    public void install(

    ) throws IOException {

        if(isInstalled()) {
            System.out.println("Your application was already installed.");
            return;
        }

        System.out.printf(
                "%sThis application is for Crow RBAC installation.%s\n",
                "\033[1;32;0m",
                "\033[0m"
                );

        String orgName = getString("`Name` of your organization");
        String email = getString("`Email` of your super administrator");
        String mobile = getString("`Phone number` of your super administrator");
        String username = getString("`Username` of your super administrator");
        String password = getString("`Password` of your super administrator");

        System.out.print("Creating your organization ... ");
        CrowOrganization org = new CrowOrganization();
        org.setName(orgName);
        org.setShortName(orgName);
        org = crowOrganizationRepository.save(org);
        System.out.println("done.");

        System.out.print("Creating super administrator account ... ");
        CrowUser su = new CrowUser();
        su.setEmail(email);
        su.setName(username);
        su.setMobile(mobile);
        su.setUsername(username);
        su.setPassword(password);
        su.setSu(true);
        su.setOrganizationId(org.getId());
        PasswordHelper.encrypt(su);
        su = crowUserRepository.save(su);
        System.out.println("done.");

        // write installation lock
        BufferedWriter lock = new BufferedWriter(new FileWriter(lockFile));
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        lock.write(sdf.format(new Date()));
        lock.close();
    }

    private String getString(String item) {
        return getString(item, true);
    }

    private String getString(String item, boolean required) {
        String info = String.format(
                "%splease input %s%s: \n",
                "\033[0;32;0m",
                "\033[0m",
                item
        );

        String input = lineReader.readLine(info);

        if(required && Strings.isNullOrEmpty(input)) {
            return getString(item, true);
        }

        return input;
    }

}
